import mx.utils.Delegate;
import core.util.*;
import util.*;

/**
 * Classe desenvolvida para centralizar o disparo de enventos relacionados a MovieClips.
 */ 
class util.ActionManager
{
	private static var actionManager:ActionManager;
	
	private function ActionManager() {}
	
	private static var valuesTable:Array;
	
	// Identico ao Swing
	public static function addActionListener(observer:ActionManagerListener, movie:MovieClip, useHandCursor:Boolean):Void
	{
		addObserver(observer, movie, "onRelease", observer.actionPerformed);
		movie["useHandCursor"] = (useHandCursor == undefined ? true : useHandCursor);
	}

	public static function getObserverTable():Array
	{
		return valuesTable;
	}

	//Identico ao Swing (So falta o metodo onClicked)
	public static function addMouseListener(observer:MouseListener, movie:MovieClip, useHandCursor:Boolean):Void
	{
		addObserver(observer, movie, "onRollOut", observer.mouseExited);
		addObserver(observer, movie, "onRollOver", observer.mouseEntered);
		addObserver(observer, movie, "onRelease", observer.mouseReleased);
		addObserver(observer, movie, "onRelease", observer.mouseClicked);
		addObserver(observer, movie, "onPress", observer.mousePressed);
		addObserver(observer, movie, "onPress", observer.mouseClicked);
		addObserver(observer, movie, "onReleaseOutside", observer.mouseReleasedOutside);
		
		movie["useHandCursor"] = (useHandCursor == undefined ? true : useHandCursor);
	}

	public static function addMouseMotionListener(observer:MouseMotionListener, movie:MovieClip):Void
	{
		addObserver(observer, movie, "onMouseMove", observer.mouseMoved);
		addObserver(observer, movie, "onDragOver", observer.mouseDragged);
	}

	public static function addFocusListener(observer:FocusListener, movie:MovieClip):Void
	{
		addObserver(observer, movie, "onKillFocus", observer.focusGained);
		addObserver(observer, movie, "onSetFocus", observer.focusLost);
	}

	public static function addKeyListener(observer:KeyListener):Void
	{
		addObserver(observer, _root, "onKeyDown", observer.keyPressed);
		addObserver(observer, _root, "onKeyUp", observer.keyReleased);
	}

	public static function addLoadObserver(observer:ActionManagerListener, movie:MovieClip):Void
	{
		addObserver(observer, movie, "onLoad", observer.actionPerformed);
		addObserver(observer, movie, "onLoadError", observer.actionPerformed);
	}

	public static function addDragObserver(observer:ActionManagerListener, movie:MovieClip):Void
	{
		addObserver(observer, movie, "onDragOut", observer.actionPerformed);
		addObserver(observer, movie, "onDragOver", observer.actionPerformed);
	}

	public static function addGlobalMouseListener(observer:ActionManagerListener):Void
	{
		addObserver(observer, _root, "onMouseMove", observer.actionPerformed);
		addObserver(observer, _root, "onMouseDown", observer.actionPerformed);
		addObserver(observer, _root, "onMouseUp", observer.actionPerformed);
	}

	public static function addFrameListener(observer:FrameListener, movie:MovieClip):Void
	{
		addObserver(observer, movie, "onEvent", observer.onFrame);
		addObserver(observer, movie, "onEnterFrame", observer.onEnterFrame);
	}

	/**
	 * Associa um observer, um movie e um commando(evento).
	 */
	private static function addObserver(observer:EventListener, movie:MovieClip, command:String, method:Function):Void
	{
		if (!valuesTable)
			start();

		// thisString eh uma variavel que armazena a string "this"
		// Foi feito assim pos o compilador recusa o comando eval("this");
		// Aqui acontece a associacao do evento do objeto com o metodo do ActionManager
		// [i]length
		//    [j]command,MovieObserver
		//             [l]movie,ObserverMethod
		//                      [k]observer, method

		movie[command] = getInstance()[command];
				

		// Percorre os comandos
		for (var i:Number = 0; i < valuesTable.length; i++)
			if (valuesTable[i][0] == command)
			{
				// Percorre os movies
				for (var j:Number = 0; j < valuesTable[i][1].length; j++)
					if (valuesTable[i][1][j][0] == movie)
					{
						// Percorre os observers
						for (var k:Number = 0; k < valuesTable[i][1][j][1].length; k++)
							if (valuesTable[i][1][j][1][k][0] == observer && valuesTable[i][1][j][1][k][1] == method)
								return;

						// Chegando aqui, significa que o observer ainda nao foi cadastrado.
						valuesTable[i][1][j][1].push(new Array(observer, method));
						return;
					}

				// Chegando aqui, significa que o movie ainda nao foi cadastrado.
				valuesTable[i][1].push(new Array(movie, new Array(new Array(observer, method))));
				return;
			}

		// Chegando aqui, significa que o comando ainda nao foi cadastrado.
		valuesTable.push(new Array(command, new Array(new Array(movie, new Array(new Array(observer, method)))), method));
	}

	// Inicializa o array
	private static function start():Void
	{	
		valuesTable = new Array();
	}

	// Desassocia um observer de um movie
	public static function removeObserver(observer:EventListener, movie:MovieClip, command:String):Void
	{
		for (var i:Number = valuesTable.length - 1; i >= 0; i--)
			if (!command || valuesTable[i][0] == command)
			{
				for (var j:Number = valuesTable[i][1].length - 1; j >=0; j--)
					if (!movie || valuesTable[i][1][j][0] == movie)
					{
						for (var k:Number = valuesTable[i][1][j][1].length - 1; k >=0; k--)
							if (!observer || valuesTable[i][1][j][1][k][0] == observer)
								valuesTable[i][1][j][1].splice(k, 1);

						if (valuesTable[i][1][j][1].length == 0)
							valuesTable[i][1].splice(j, 1);
					}

				if (valuesTable[i][1].length == 0)
					valuesTable.splice(i, 1);
			}
	}
	
	public static function removeMouseListener(observer:EventListener, movie:MovieClip)
	{
		removeObserver(observer, movie, "onRollOut");
		removeObserver(observer, movie, "onRollOver");
		removeObserver(observer, movie, "onRelease");
		removeObserver(observer, movie, "onPress");
		removeObserver(observer, movie, "onReleaseOutside");
	}
	
	public static function removeMouseMotionListener(observer:EventListener, movie:MovieClip)
	{
		removeObserver(observer, movie, "onMouseMove");
		removeObserver(observer, movie, "onDragOver");
	}
	
	public static function removeFrameListener(observer:EventListener, movie:MovieClip)
	{
		removeObserver(observer, movie, "onEvent");
		removeObserver(observer, movie, "onEnterFrame");
	}	
	
	// quando um evento eh disparado a classe analisa e distribui para os objetos a ele cadastrado
	public static function actionPerformed(command:String, methodArguments:Array, object:Object):Void 
	{
		var event:ActionEvent = new ActionEvent(command, methodArguments, object);
 
		for (var i:Number = 0; i < valuesTable.length; i++)
			if (valuesTable[i][0] == command)
				for (var j:Number = 0; j < valuesTable[i][1].length; j++)
					if (valuesTable[i][1][j][0] == object)
						for (var k:Number = 0; k < valuesTable[i][1][j][1].length; k++)
						{
							var newObject:Object = ActionManager.valuesTable[i][1][j][1][k][0];
							var method:Function = Function(ActionManager.valuesTable[i][1][j][1][k][1]);

							method.apply(newObject, [event]);
						}
	}

	public function onDragOver():Void { ActionManager.actionPerformed("onDragOver", arguments, this); }

	public function onDragOut():Void { ActionManager.actionPerformed("onDragOut", arguments, this); }

	public function onEvent():Void {	ActionManager.actionPerformed("onEvent", arguments, this); }
	
	public function onEnterFrame():Void { ActionManager.actionPerformed("onEnterFrame", arguments, this); }

	public function onKeyUp():Void { ActionManager.actionPerformed("onKeyUp", arguments, this); }

	public function onKeyDown():Void { ActionManager.actionPerformed("onKeyDown", arguments, this); }

	public function onKillFocus():Void { ActionManager.actionPerformed("onFillFocus", arguments, this); }

	public function onLoad():Void { ActionManager.actionPerformed("onLoad", arguments, this); }

	public function onLoadError():Void { ActionManager.actionPerformed("onLoadError", arguments, this); }

	public function onMouseMove():Void { ActionManager.actionPerformed("onMouseMove", arguments, this); }

	public function onMouseDown():Void { ActionManager.actionPerformed("onMouseDown", arguments, this); }

	public function onMouseUp():Void { ActionManager.actionPerformed("onMouseUp", arguments, this); }

	public function onPress():Void { ActionManager.actionPerformed("onPress", arguments, this); }

	public function onRelease():Void	{ ActionManager.actionPerformed("onRelease", arguments, this); }

	public function onReleaseOutside():Void { ActionManager.actionPerformed("onReleaseOutside", arguments, this);}

	public function onRollOver():Void { ActionManager.actionPerformed("onRollOver", arguments, this); }

	public function onRollOut():Void { ActionManager.actionPerformed("onRollOut", arguments, this); }

	public function onSetFocus():Void { ActionManager.actionPerformed("onSetFocus", arguments, this); }

	public static function getInstance():ActionManager
	{
		if (actionManager)
			return actionManager;
		else
		{
			actionManager = new ActionManager();	
			return actionManager;
		}
	}
}